# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor, task, defer, protocol
import struct
import json
from flask import Flask, request, jsonify, g, render_template, redirect, url_for, session, current_app
from twisted.python import log
import sys
import time
import txredisapi as redis
import pika
from pika.adapters import twisted_connection
import os

import sys
sys.path.append("../")

from app.model import User, vnf, db_session, gameInfo
from  customConfig.msg import command

serverCommand = command['serverCommand']

log.startLogging(sys.stdout)

REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 4
REDIS_PASSWORD = 'admin'

RABBITMQ_HOST = 'localhost'
RABBITMQ_PORT = 5672
RABBITMQ_USERNAME = 'admin'
RABBITMQ_PASSWORD = 'admin'

HOST_IP = ""

redis_store = redis.lazyConnectionPool(dbid=4, host='localhost', port=6379, password='admin')


@defer.inlineCallbacks
def check_token(phone_number, token):
    token_in_redis = yield redis_store.hget('user:%s' % phone_number, 'token')
    if token != token_in_redis:
        defer.returnValue(False)
    else:
        defer.returnValue(True)


class RabbitMQ(object):
    _connection = None
    _channel_receive_from_http = None

    @staticmethod
    @defer.inlineCallbacks
    def init_mq(ip_address, port):
        credentials = pika.PlainCredentials(RABBITMQ_USERNAME, RABBITMQ_PASSWORD)
        parameters = pika.ConnectionParameters(credentials=credentials)
        cc = protocol.ClientCreator(reactor, twisted_connection.TwistedProtocolConnection, parameters)
        RabbitMQ._connection = yield cc.connectTCP(ip_address, port)
        defer.returnValue(1)

    @staticmethod
    @defer.inlineCallbacks
    def set_channel_receive_from_back(user_factory):
        """
        设置rabbitmq消息接受队列的channel，并且做好循环任务
        """
        RabbitMQ._channel_receive_from_http = yield RabbitMQ._connection.channel()
        yield RabbitMQ._channel_receive_from_http.queue_declare(queue='vnfDemo')
        queue_object, consumer_tag = yield RabbitMQ._channel_receive_from_http.basic_consume(queue='vnfDemo', no_ack=True)
        l = task.LoopingCall(RabbitMQ.read_from_mq, queue_object, user_factory)
        l.start(0.5)
        defer.returnValue(1)

    @staticmethod
    @defer.inlineCallbacks
    def read_from_mq(queue_object, chat_factory):
        """
        读取接受到的消息队列消息，并且处理
        """
        ch, method, properties, body = yield queue_object.get()

        if body:
            log.msg('Accept data from http successful!')
            chat_factory.process_data_from_mq(body)
            defer.returnValue(1)
        defer.returnValue(0)


class Chat(Protocol):
    def __init__(self, factory):
        self.factory = factory
        self.phone_number = None
        self.state = "VERIFY"
        self.version = 0
        self.last_heartbeat_time = 0
        self.ip = None
        self.port = None
        self.host_ip = None

        self.command_func_dict = {
            serverCommand.ONLINE_REPORT: self.handle_online_report,
            serverCommand.SINGLE_NOTICE: self.handle_single_chat,
            serverCommand.GROUP_NOTICE: self.handle_group_chat,
            serverCommand.BROADCAST_NOTICE: self.handle_broadcast_chat,
            serverCommand.HEARTBEAT: self.handle_heartbeat,
            serverCommand.HOST_REPORT: self.handle_host_report
        }
        self._data_buffer = bytes()

    def connectionMade(self):
        log.msg("New connection, the info is:", self.transport.getPeer())

    def connectionLost(self, reason):
        log.msg("[%s]:断线" % self.phone_number.encode('utf-8'))
        if self.phone_number in self.factory.users:
            del self.factory.users[self.phone_number]

    def dataReceived(self, data):
        """
        接受到数据以后的操作
        """

        self._data_buffer += data
        while True:
            length, self.version, command_id = struct.unpack('!3I', self._data_buffer[:12])

            if length > len(self._data_buffer):
                return

            content = self._data_buffer[12:length]

            if command_id not in [1, 2, 3, 4, 5, 6]:
                return

            log.msg("-------call dataReceived--------")
            if self.state == "VERIFY" and command_id == 1:
                self.handle_online_report(content)
            elif self.state == "VERIFY" and command_id == 6:
                self.handle_host_report(content)

            if self.state == "DATA":
                self.handle_data(command_id, content)

            self._data_buffer = self._data_buffer[length:]

            if len(self._data_buffer) < 12:
                return

    def handle_heartbeat(self, content):
        """
        处理心跳包
        """
        self.last_heartbeat_time = int(time.time())


    #@defer.inlineCallbacks
    def handle_online_report(self, content):
        """
        设备上线通知
        """
        content = json.loads(content)
        ip = content.get('ip')
        #port = content.get('port')
        mac = content.get('mac')
        game_info = content.get("game_info")

        log.msg("欢迎, %s,%s!" % (ip.encode('utf-8'), mac.encode('utf-8')) )
        #log.msg("port=%s, mac=%s" % (port.encode('utf-8'), mac.encode('utf-8')))
        log.msg("game_info=%s" %game_info)

        self.ip = ip
        self.factory.vnfLists[ip] = self
        self.state = "DATA"

        new_vnf = vnf(ip=ip, mac=mac)
        log.msg("new_vnf=", new_vnf)

        report_ret = 0

        #检查新上线的vnf是否在数据库中已存在?
        vnf_ret = db_session.query(vnf).filter(vnf.mac == mac).all()
        log.msg("vnf-->%s" %vnf_ret)
        if not vnf_ret:
            log.msg('No same device found in database! save this device!')
            db_session.add(new_vnf)
            try:
                db_session.commit()
                report_ret = 0
            except Exception as e:
                db_session.rollback()
                log.msg(e)
                report_ret = 1
        else:
            log.msg('Find same device, same game_info?')
            vnf_db_obj = db_session.query(vnf).filter(vnf.mac == mac)
            vnf_data = db_session.query(vnf).filter(vnf.mac == mac).all()[0]
            log.msg("vnf_data.gameIdsList-->%s" %vnf_data.gameIdsList)
            game_info_db = db_session.query(gameInfo)

            games_info_list = game_info["games_info"]
            log.msg("games_info_list-->%s" %games_info_list)

            game_ids_str = ""
            game_ids_list = []

            for game_info in games_info_list:
                game_exec_file = game_info.get("executable_file")
                log.msg("game_exec_file--->%s" %game_exec_file)
                game_exec_file = game_exec_file.split("\\")[-1]
                game_db_obj = game_info_db.filter(gameInfo.executeFile == game_exec_file)
                log.msg(len(game_db_obj.all()))
                #find the game in batabase
                if game_db_obj and len(game_db_obj.all()) >= 1:
                    game_ids_list.append(game_db_obj.all()[0].id)

            game_ids_str = ",".join(map(str, game_ids_list))
            log.msg("game_ids_str-->%s" %game_ids_str)
            vnf_db_obj.update({vnf.gameIdsList: game_ids_str})
            try:
                db_session.commit()
            except Exception as e:
                db_session.rollback()

        send_content = json.dumps({'code': report_ret})

        self.send_content(send_content, 101, [ip])

    #处理host上报正在运行的vnf信息
    def handle_host_report(self, content):
        """
        host上报正在运行的vnf
        """

        #log.msg("in ....type(content)-->%s" %type(content))

        content = json.loads(content.encode('utf-8'))
        #log.msg("after json.loads(content)-->%s" %type(content))

        vnf_info = content.get('vnf_info')
        host_ip = content.get("host_ip").encode('utf-8')
        HOST_IP = host_ip
        #log.msg("vnf_info--->%s" %vnf_info)
        #log.msg("type(vnf_info)-->%s" % type(vnf_info))

        #log.msg("host_ip--->%s" %host_ip)


        self.ip = HOST_IP
        self.host_ip = HOST_IP
        self.factory.vnfLists[HOST_IP] = self
        self.state = "DATA"

        report_ret = 0

        #检查新上线的vnf是否在数据库中已存在?
        vnf_query = db_session.query(vnf)
        for vnf_item in vnf_info:
            log.msg("<<<<<<(vnf_item)>>>>>>-->%s" %vnf_item)

            log.msg("type(vnf_item)-->%s" %type(vnf_item))
            #vnf_item = json.loads(vnf_item)
            #vnf_item = json.dumps(vnf_item).decode('unicode-escape')

            vnf_ip = ""
            vnf_mac = ""
            vnf_port = ""
            if vnf_item.has_key("ip") and vnf_item.get("ip"):
                vnf_ip = vnf_item.get("ip").encode('utf-8')
                if not check_same_subnet(vnf_ip, HOST_IP):
                    continue
            else:
                log.msg("ip is empty! continue loop!")
                continue

            if vnf_item.has_key("mac") and vnf_item.get("mac") != None:
                vnf_mac = vnf_item.get("mac").encode('utf-8')
            if vnf_item.has_key("port") and vnf_item.get("ip") != None:
                vnf_port = vnf_item.get("port").encode('utf-8')


            log.msg("vnf_ip->%s, vnf_mac=%s, vnf_port->%s" %(vnf_ip,vnf_mac,vnf_port))

            if vnf_query.filter(vnf.mac == vnf_mac).all():
                log.msg("same vnf exists, update value!")
                vnf_db_obj = vnf_query.filter(vnf.mac == vnf_mac)
                vnf_data = vnf_query.filter(vnf.mac == vnf_mac).all()[0]
                log.msg("get data---->vnf_data=%s" %vnf_data)
                log.msg("get data---->vnf_data.ip=%s" % vnf_data.ip)

                if vnf_data.ip != vnf_ip:
                    vnf_db_obj.update({vnf.ip: vnf_ip})
                    try:
                        db_session.commit()
                    except Exception as e:
                        db_session.rollback()
                else:
                    log.msg("same vnf info, no need to change")
                if vnf_data.port != vnf_port:
                    vnf_db_obj.update({vnf.port: vnf_port})
                    try:
                        db_session.commit()
                    except Exception as e:
                        db_session.rollback()
                if vnf_data.spiceIp != HOST_IP:
                    vnf_db_obj.update({vnf.spiceIp: HOST_IP})
                    try:
                        db_session.commit()
                    except Exception as e:
                        db_session.rollback()

                else:
                    log.msg("same vnf info, no need to change")
            else:
                log.msg("------ add new vnf!!")
                new_vnf = vnf(ip=vnf_ip, mac=vnf_mac, port=vnf_port, spiceIp=HOST_IP)
                db_session.add(new_vnf)
                try:
                    db_session.commit()
                    log.msg("add success?")
                except Exception as e:
                    log.msg("add error!")
                    log.msg(Exception)
                    log.msg(e)
                    db_session.rollback()


        # vnf_ret = db_session.query(vnf).filter(vnf.ip == ip and vnf.port == port).all()
        # log.msg("vnf-->%s" %vnf_ret)
        # if not vnf_ret:
        #     log.msg('No same device found in database! save this device!')
        #     db_session.add(new_vnf)
        #     try:
        #         db_session.commit()
        #         report_ret = 0
        #     except Exception as e:
        #         db_session.rollback()
        #         report_ret = 1
        # else:
        #     log.msg('Find same device, please the ip and port right?')

        send_content = json.dumps({'code': report_ret})

        self.send_content(send_content, 105, [host_ip])

    def handle_data(self, command_id, content):
        """
        根据command_id来分配函数
        """
        self.command_func_dict[command_id](content)

    def handle_single_chat(self, content):
        """
        单播
        """
        content = json.loads(content)
        chat_from = content.get('chat_from')
        chat_to = content.get('chat_to')
        chat_content = content.get('chat_content')
        send_content = json.dumps(dict(chat_from=chat_from, chat_content=chat_content))

        self.send_content(send_content, 102, [chat_to])

    def handle_group_chat(self, content):
        """
        组播
        """
        content = json.loads(content)
        chat_from = content.get('chat_from')
        chat_to = content.get('chat_to')
        chat_content = content.get('chat_content')
        send_content = json.dumps(dict(chat_from=chat_from, chat_content=chat_content))

        phone_numbers = chat_to
        self.send_content(send_content, 103, phone_numbers)

    def handle_broadcast_chat(self, content):
        """
        广播
        """
        content = json.loads(content)
        #chat_from = content.get('chat_from')
        ps_list = content.get('ps_list')
        log.msg("server get ps_list-->%s" %ps_list)

        game_db_obj = db_session.query(gameInfo)
        game_data = db_session.query(gameInfo).all()
        for ps_list_item in ps_list:
            ps_name = ps_list_item.get("name")

            game_ret = game_db_obj.filter(gameInfo.executeFile == ps_name)
            if game_ret != None or game_ret != "":
                log.msg("vnf has game running!")
                return
            else:
                continue



        #send_content = json.dumps(dict(chat_from=chat_from, chat_content=chat_content))

        #phone_numbers = self.factory.users.keys()
        #self.send_content(send_content, 104, phone_numbers)

    def send_content(self, send_content, command_id, ipLists):
        """
        发送函数
        """
        length = 12 + len(send_content)
        version = self.version
        command_id = command_id
        header = [length, version, command_id]
        header_pack = struct.pack('!3I', *header)

        for ipAddr in ipLists:
            if ipAddr in self.factory.vnfLists.keys():
                self.factory.vnfLists[ipAddr].transport.write(header_pack + send_content)
            else:
                log.msg("device :%s 不在线." % ipAddr.encode('utf-8'))

    # def send_content(self, send_content, command_id, ipLists):
    #     """
    #     发送函数
    #     """
    #     length = 12 + len(send_content)
    #     version = self.version
    #     command_id = command_id
    #     header = [length, version, command_id]
    #     header_pack = struct.pack('!3I', *header)
    #
    #     log.msg('self.factory.vnfLists.keys()-->%s' %self.factory.vnfLists.keys())
    #
    #
    #     # for ip in ipLists:
    #     #     if ip in self.factory.vnfLists.keys():
    #     #         log.msg("find ip--->ip=%s" %ip)
    #     #         log.msg("self.factory.vnfLists[ip]-->%s" %self.factory.vnfLists[ip])
    #     #         self.factory.vnfLists[ip].transport.write(header_pack + send_content)
    #     #     else:
    #     #         log.msg("deivce ip:%s 不在线." % ip)
    #
    #     phone_numbers = ["13565208554"]
    #     for phone_number in phone_numbers:
    #         if phone_number in self.factory.users.keys():
    #             self.factory.users[phone_number].transport.write(header_pack + send_content)
    #         else:
    #             log.msg("Phone_number:%s 不在线." % phone_number.encode('utf-8'))


class ChatFactory(Factory):
    def __init__(self):
        self.users = {}
        self.vnfLists = {}
        self.host = {}

    def buildProtocol(self, addr):
        return Chat(self)

    def check_users_online(self):
        for key, value in self.users.items():
            if value.last_heartbeat_time != 0 and int(time.time()) - value.last_heartbeat_time > 4:
                log.msg("[%s]没有检测到心跳包,主动切断" % key.encode('utf-8'))
                value.transport.abortConnection()

    @defer.inlineCallbacks
    def receive_from_mq(self):
        data = yield redis_store.rpop('vnfDemo')
        if data:
            log.msg("接受到来自消息队列的消息:", data)
            self.process_data_from_mq(data)

    def process_data_from_mq(self, data):
        loads_data = json.loads(data)
        command_id = loads_data.get('command_id')
        phone_numbers = loads_data.get('chat_to')
        chat_from = loads_data.get('chat_from')
        chat_content = loads_data.get('chat_content')

        log.msg("process_data_from_mq-->command_id=%s, phone_numbers=%s, chat_from=%s, chat_content=%s" %(command_id,
                phone_numbers, chat_from, chat_content))


        content = json.dumps(dict(chat_from=chat_from, chat_content=chat_content))
        ipLists = []
        #给所有设备发送消息
        if command_id == 104:
            vnf_ret = db_session.query(vnf).filter(vnf.ip).all()
            for item in vnf_ret:
                log.msg("item-->%s" %item)
                log.msg("item.ip-->%s" % item.ip)
                vnf_ip = item.ip.encode("utf-8")
                log.msg('vnf_ip--->%s' %vnf_ip)


                if vnf_ip != None or vnf_ip != "":
                    ipLists.append(vnf_ip)
                else:
                    continue

        log.msg("ipLists--->%s" %ipLists)



        self.send_content(content, command_id, ipLists)

    def send_content(self, send_content, command_id, ipLists):
        """
        发送函数
        """
        length = 12 + len(send_content)
        version = 1100
        command_id = command_id
        header = [length, version, command_id]
        header_pack = struct.pack('!3I', *header)


        for ipAddr in ipLists:
            if ipAddr in self.vnfLists.keys():
                self.vnfLists[ipAddr].transport.write(header_pack + send_content)
            else:
                log.msg("device ipAddr:%s 不在线." % ipAddr.encode('utf-8'))

    # def send_content(self, send_content, command_id, phone_numbers):
    #     """
    #     发送函数
    #     """
    #     length = 12 + len(send_content)
    #     version = 1100
    #     command_id = command_id
    #     header = [length, version, command_id]
    #     header_pack = struct.pack('!3I', *header)
    #     for phone_number in phone_numbers:
    #         if phone_number in self.users.keys():
    #             self.users[phone_number].transport.write(header_pack + send_content)
    #         else:
    #             log.msg("Phone_number:%s 不在线." % phone_number.encode('utf-8'))

def check_same_subnet(ip1, ip2):
    log.msg("ip1-->%s, ip2--->%s" %(ip1, ip2))
    ip1_arr = ip1.split(".")
    ip1_arr = map(str, ip1_arr)
    ip1_arr.pop(-1)

    ip1_subnet_prefix = ".".join(ip1_arr)
    log.msg("ip1_subnet_prefix-->%s" %ip1_subnet_prefix)

    ip2_arr = ip2.split(".")
    ip2_arr = map(str, ip2_arr)
    ip2_str = ".".join(ip2_arr)
    log.msg('ip2_str-->%s' %ip2_str)

    if ip2_str.find(ip1_subnet_prefix) != -1:
        log.msg("same subnet!")
        return True

    return False

cf = ChatFactory()

task1 = task.LoopingCall(cf.check_users_online)
task1.start(3, now=False)

task_receive_data_from_mq = task.LoopingCall(cf.receive_from_mq)
task_receive_data_from_mq.start(0.1, now=False)

reactor.callLater(0.1, RabbitMQ.init_mq, RABBITMQ_HOST, RABBITMQ_PORT)

reactor.callLater(0.5, RabbitMQ.set_channel_receive_from_back, cf)

reactor.listenTCP(8124, cf)
reactor.run()